WPF MVVM应用程序中的键盘事件? 您所在的位置:网站首页 wpf 事件绑定 mvvm WPF MVVM应用程序中的键盘事件?

WPF MVVM应用程序中的键盘事件?

2024-07-11 07:56| 来源: 网络整理| 查看: 265

如何在不使用代码隐藏的情况下处理Keyboard.KeyDown事件? 我们正在尝试使用MVVM模式,并避免在代码隐藏文件中编写事件处理程序。

相关讨论 为了节省搜索者阅读响应的时间,从4/2011开始的快速答案是,如果没有代码,这是不可能的。 原因是-KeyDown是EventTrigger,但DataTriggers仅支持条件。 这可以通过一个简单的转换器轻松解决,但是当前只有XAML无法实现,除非您创建一个设置DataTrigger的EventTrigger-但是,如果这样做,则对代码的可读性说再见。 这里的主要答案仅适用于单键捕获(例如" Enter"键)。 一个更完整和最新的答案在这里:stackoverflow.com/a/38433681/93394。 (使用Windows Interactivity库中的EventTriggers来响应任何KeyDown事件。)

为了提供更新的答案,.net 4.0框架允许您将KeyBinding Command绑定到视图模型中的命令,从而使您可以很好地做到这一点。

所以...如果您想听Enter键,则可以执行以下操作:

12345678                 相关讨论 从.net 4开始,这绝对是正确的方法。需要更多支持 顺便说一句,这也适用于鼠标单击。示例: 另一个注意事项:这不适用于TextBlock,因为TextBlock不能聚焦。 InputBinding需要在层次结构中较高的对象上设置。 因此,正如OP最初询问的那样,您如何将其链接到输入的任何文本?就是OnKeyDown事件? 这会消耗输入内容,是否仍然会发出仅响应按键预览的命令,以便仍会发生与该按键有关的正常功能?例如,如果我想在某个控件上按下Tab键时运行命令,但我仍然希望Tab的默认行为能够正常运行(将焦点按Tab顺序移至下一个控件)。在按Tab键时,对Tab键使用此键绑定不再移动焦点。

哇-有大约一千个答案,在这里我要添加另一个答案。

"为什么不实现这种前额拍击"的方式中最明显的事情是,后面的代码和ViewModel坐在同一房间里,可以这么说。没有理由不让他们进行对话。

如果您考虑一下,那么XAML已经与ViewModel的API紧密耦合,因此您最好还是从后面的代码中对其进行依赖。

遵循或忽略的其他明显规则仍然适用(接口,空检查

我总是在这样的代码背后创建一个属性:

1private ViewModelClass ViewModel { get { return DataContext as ViewModelClass; } }

这是客户端代码。空检查用于帮助控制混合主机。

123456void someEventHandler(object sender, KeyDownEventArgs e) {     if (ViewModel == null) return;     /* ... */     ViewModel.HandleKeyDown(e); }

按需要在背后的代码中处理事件(UI事件以UI为中心,所以可以),然后在ViewModelClass上有一个可以响应该事件的方法。担忧仍然分开。

1234ViewModelClass {     public void HandleKeyDown(KeyEventArgs e) { /* ... */ } }

所有其他这些附加属性和伏都教非常酷,这些技术对于其他一些事情确实很有用,但是在这里您可能会遇到一些更简单的事情……

相关讨论 始终获取ViewModel的本地副本,而不是每次都调用该属性... var vm = ViewModel;,从那里使用vm代替ViewModel。

有点晚了,但是这里。

微软的WPF团队最近发布了他们的WPF MVVM Toolkit的早期版本 。在其中,您将找到一个名为CommandReference的类,该类可以处理诸如键盘绑定之类的事情。查看他们的WPF MVVM模板以了解其工作原理。

相关讨论 我昨天刚刚尝试过,我认为这是绑定密钥的最干净的方法。不幸的是,无法同时绑定所有键(或者我无法弄清楚如何绑定),而且我需要整个键盘,因此您必须为每个键创建一个绑定,然后在按下shift键时再次创建所有键,然后再按一次Ctrl ...会很长。我们选择在代码中仅包含一个KeyUp处理程序,以在VM中调用方法。喜欢5行代码,而不是所有这些绑定。而且VM仍然不知道View。感谢您的回应 2020更新。 WPF已死,并且以上链接中的帖子已存档并放置在此处archive.codeplex.com/?p=wpf

我通过使用具有3个依赖项属性的附加行为来完成此操作;一个是要执行的命令,一个是要传递给命令的参数,另一个是将导致命令执行的键。这是代码:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156public static class CreateKeyDownCommandBinding {     ///     /// Command to execute.     ///     public static readonly DependencyProperty CommandProperty =         DependencyProperty.RegisterAttached("Command",         typeof(CommandModelBase),         typeof(CreateKeyDownCommandBinding),         new PropertyMetadata(new PropertyChangedCallback(OnCommandInvalidated)));     ///     /// Parameter to be passed to the command.     ///     public static readonly DependencyProperty ParameterProperty =         DependencyProperty.RegisterAttached("Parameter",         typeof(object),         typeof(CreateKeyDownCommandBinding),         new PropertyMetadata(new PropertyChangedCallback(OnParameterInvalidated)));     ///     /// The key to be used as a trigger to execute the command.     ///     public static readonly DependencyProperty KeyProperty =         DependencyProperty.RegisterAttached("Key",         typeof(Key),         typeof(CreateKeyDownCommandBinding));     ///     /// Get the command to execute.     ///     ///     ///     public static CommandModelBase GetCommand(DependencyObject sender)     {         return (CommandModelBase)sender.GetValue(CommandProperty);     }     ///     /// Set the command to execute.     ///     ///     ///     public static void SetCommand(DependencyObject sender, CommandModelBase command)     {         sender.SetValue(CommandProperty, command);     }     ///     /// Get the parameter to pass to the command.     ///     ///     ///     public static object GetParameter(DependencyObject sender)     {         return sender.GetValue(ParameterProperty);     }     ///     /// Set the parameter to pass to the command.     ///     ///     ///     public static void SetParameter(DependencyObject sender, object parameter)     {         sender.SetValue(ParameterProperty, parameter);     }     ///     /// Get the key to trigger the command.     ///     ///     ///     public static Key GetKey(DependencyObject sender)     {         return (Key)sender.GetValue(KeyProperty);     }     ///     /// Set the key which triggers the command.     ///     ///     ///     public static void SetKey(DependencyObject sender, Key key)     {         sender.SetValue(KeyProperty, key);     }     ///     /// When the command property is being set attach a listener for the     /// key down event.  When the command is being unset (when the     /// UIElement is unloaded for instance) remove the listener.     ///     ///     ///     static void OnCommandInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)     {         UIElement element = (UIElement)dependencyObject;         if (e.OldValue == null && e.NewValue != null)         {             element.AddHandler(UIElement.KeyDownEvent,                 new KeyEventHandler(OnKeyDown), true);         }         if (e.OldValue != null && e.NewValue == null)         {             element.RemoveHandler(UIElement.KeyDownEvent,                 new KeyEventHandler(OnKeyDown));         }     }     ///     /// When the parameter property is set update the command binding to     /// include it.     ///     ///     ///     static void OnParameterInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)     {         UIElement element = (UIElement)dependencyObject;         element.CommandBindings.Clear();         // Setup the binding         CommandModelBase commandModel = e.NewValue as CommandModelBase;         if (commandModel != null)         {             element.CommandBindings.Add(new CommandBinding(commandModel.Command,             commandModel.OnExecute, commandModel.OnQueryEnabled));         }     }     ///     /// When the trigger key is pressed on the element, check whether     /// the command should execute and then execute it.     ///     ///     ///     static void OnKeyDown(object sender, KeyEventArgs e)     {         UIElement element = sender as UIElement;         Key triggerKey = (Key)element.GetValue(KeyProperty);         if (e.Key != triggerKey)         {             return;         }         CommandModelBase cmdModel = (CommandModelBase)element.GetValue(CommandProperty);         object parameter = element.GetValue(ParameterProperty);         if (cmdModel.CanExecute(parameter))         {             cmdModel.Execute(parameter);         }         e.Handled = true;     } }

要从xaml使用此功能,您可以执行以下操作:

123     Enter

编辑:CommandModelBase是我用于所有命令的基类。它基于Dan Crevier的MVVM文章(此处)中的CommandModel类。这是我与CreateKeyDownCommandBinding一起使用的经过稍微修改的版本的来源:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354public abstract class CommandModelBase : ICommand     {         RoutedCommand routedCommand_;         ///         /// Expose a command that can be bound to from XAML.         ///         public RoutedCommand Command         {             get { return routedCommand_; }         }         ///         /// Initialise the command.         ///         public CommandModelBase()         {             routedCommand_ = new RoutedCommand();         }         ///         /// Default implementation always allows the command to execute.         ///         ///         ///         public void OnQueryEnabled(object sender, CanExecuteRoutedEventArgs e)         {             e.CanExecute = CanExecute(e.Parameter);             e.Handled = true;         }         ///         /// Subclasses must provide the execution logic.         ///         ///         ///         public void OnExecute(object sender, ExecutedRoutedEventArgs e)         {             Execute(e.Parameter);         }         #region ICommand Members         public virtual bool CanExecute(object parameter)         {             return true;         }         public event EventHandler CanExecuteChanged;         public abstract void Execute(object parameter);         #endregion     }

欢迎提出改进意见和建议。

相关讨论 谢谢保罗,我将尝试一下。 之前已经有人问过这个问题,但是CommandModelBase是从哪里来的呢?我想我错过了一些东西... 香港专业教育学院更新了答案,以包括CommandModelBase的描述。我本来应该把它包括在内。希望能帮助到你!

几个月前,我研究了这个问题,并编写了一个标记扩展来解决问题。它可以像常规绑定一样使用:

123    

此扩展程序的完整源代码可以在这里找到:

[WPF] Using InputBindings with the MVVM pattern

请注意,此解决方法可能不是很"干净",因为它通过反射使用了一些私有类和字段...

简短的答案是,如果没有代码隐藏,您将无法处理直接的键盘输入事件,但是您可以使用MVVM处理InputBindings(如果您需要的话,我可以向您展示相关示例)。

您能否提供有关您要在处理程序中执行的操作的更多信息?

MVVM并不能完全避免代码落后。它仅用于严格与UI相关的任务。一个重要的例子是具有某种类型的"数据输入表单",在加载时,需要将焦点设置到第一个输入元素(文本框,组合框等)上。您通常会为该元素分配一个x:Name属性,然后将Window / Page / UserControl的" Loaded"事件挂接起来以将焦点设置到该元素。通过该模式这完全可以实现,因为该任务是以UI为中心的,并且与它表示的数据无关。

与karlipoppins答案类似,但是我发现如果没有以下添加/更改,它将无法正常工作:

12345                

我知道这个问题已经很老了,但是我之所以这样做,是因为这种类型的功能只是在Silverlight(5)中更易于实现。 所以也许其他人也会来这里。

在找不到所需的内容之后,我写了这个简单的解决方案。 原来这很简单。 它应该在Silverlight 5和WPF中都可以工作。

1234567891011121314151617181920212223242526272829303132333435363738public class KeyToCommandExtension : IMarkupExtension {     public string Command { get; set; }     public Key Key { get; set; }     private void KeyEvent(object sender, KeyEventArgs e)     {         if (Key != Key.None && e.Key != Key) return;         var target = (FrameworkElement)sender;         if (target.DataContext == null) return;         var property = target.DataContext.GetType().GetProperty(Command, BindingFlags.Public | BindingFlags.Instance, null, typeof(ICommand), new Type[0], null);         if (property == null) return;         var command = (ICommand)property.GetValue(target.DataContext, null);         if (command != null && command.CanExecute(Key))             command.Execute(Key);     }     public Delegate ProvideValue(IServiceProvider serviceProvider)     {         if (string.IsNullOrEmpty(Command))             throw new InvalidOperationException("Command not set");         var targetProvider = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));         if (!(targetProvider.TargetObject is FrameworkElement))             throw new InvalidOperationException("Target object must be FrameworkElement");         if (!(targetProvider.TargetProperty is EventInfo))             throw new InvalidOperationException("Target property must be event");         return Delegate.CreateDelegate(typeof(KeyEventHandler), this,"KeyEvent");     }

用法:

1

请注意,Command是字符串,而不是可绑定的ICommand。 我知道这不那么灵活,但是使用起来更干净,而且您有99%的时间需要。 虽然更改应该不是问题。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有